﻿using System;
using System.Data.SqlClient;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceProcess;
using System.Threading;
using DM4;
using sscore;

using System.ServiceModel.Web;
using System.Data.Services;
using System.Data.EntityClient;

namespace DM4SRV
{
    public partial class DM4SRV : ServiceBase
    {
        private ServiceHost _serviceHost;
        private WebServiceHost _serviceLogHost;
        private readonly String _workDir;
        private readonly String _cacheDir;
        private readonly String _logDir;
        private readonly String _daemonsDir;
        private readonly String _modulesDir;
        private readonly String _scriptsDir;
        private readonly String _settingsDir;
        private readonly String _assembliesDir;

        private Log _sysLog;
        private Params _prm;
        private SqlConnection _conn;

        private DM4.DM4 _dm4;

        public DM4SRV()
        {
            InitializeComponent();

            String path = Assembly.GetExecutingAssembly().Location;
            _workDir = Path.GetDirectoryName(path);

            _logDir = String.Format("{0}\\{1}", _workDir, "log");
            _cacheDir = String.Format("{0}\\{1}", _workDir, "cache");
            _daemonsDir = String.Format("{0}\\{1}", _workDir, "daemons");
            _modulesDir = String.Format("{0}\\{1}", _workDir, "modules");
            _scriptsDir = String.Format("{0}\\{1}", _workDir, "scripts");
            _settingsDir = String.Format("{0}\\{1}", _workDir, "settings");
            _assembliesDir = _workDir;

            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.AssemblyResolve += ResolveEventHandler;
        }

        private Assembly ResolveEventHandler(object sender, ResolveEventArgs args)
        {
            //This handler is called only when the common language runtime tries to bind to the assembly and fails.

            //Retrieve the list of referenced assemblies in an array of AssemblyName.
            string strTempAssmbPath = "";

            Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
            AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();

            //Loop through the array of referenced assembly names.
            foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
            {
                //Check for the assembly names that have raised the "AssemblyResolve" event.
                if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",", StringComparison.Ordinal)) == args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal)))
                {
                    //Build the path of the assembly from where it has to be loaded.				
                    strTempAssmbPath = String.Format("{0}\\{1}.dll", _assembliesDir, args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.Ordinal)));
                    break;
                }
            }
            //Load the assembly from the specified path. 					
            Assembly myAssembly = Assembly.LoadFrom(strTempAssmbPath);

            //Return the loaded assembly.
            return myAssembly;
        }

        public void WriteLog(string message, MessageType messageType = MessageType.Info)
        {
            _sysLog.WriteLog(message, messageType);
        }

        void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            HandleException((Exception)e.ExceptionObject);
        }

        void HandleException(Exception e)
        {
            EventLog.WriteEntry(e.Message);
        }

        protected override void OnStart(string[] args)
        {
            EventLog.WriteEntry("Start DM4 service...");
            EventLog.WriteEntry("Check local directories...");

            AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;

            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

            DirectoryInfo directory = new DirectoryInfo(_logDir);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(_cacheDir);
            if (!directory.Exists) directory.Create();
            else
            {
                directory.Delete(true);
                directory.Create();
            }

            directory = new DirectoryInfo(_daemonsDir);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(_modulesDir);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(_scriptsDir);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(_settingsDir);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(_assembliesDir);
            if (!directory.Exists) directory.Create();

            EventLog.WriteEntry("Switch to local log file");
            try
            {
                _sysLog = Log.CreateLogWithoutDataset(String.Format("{0}\\{1}", _workDir, "dm4.log"),
                                                             EncodingType.Utf8, LogMode.Full, false);
                WriteLog("Begin start DM4 service...");

                string paramsFileName = String.Format("{0}\\{1}", _workDir, "dm4config.xml");
                WriteLog(String.Format("Config file: {0}", paramsFileName));

                _prm = Params.CreateNonCryptedParamsWithoutEtalon(paramsFileName);
                WriteLog("Load parameters...");

                if (_prm.GetBooleanParam("UseDatabase"))
                {
                    WriteLog("Check database connection...");
                    _conn = new SqlConnection(_prm.GetStringParam("ConnectionString"));
                    _conn.Open();
                    WriteLog("DB connection test successfully");
                }

                //set current directories
                _prm.SetStringParam("DaemonsDir", _daemonsDir);
                _prm.SetStringParam("ModulesDir", _modulesDir);
                _prm.SetStringParam("ScriptsDir", _scriptsDir);
                _prm.SetStringParam("SettingsDir", _settingsDir);
                _prm.SetStringParam("LogDir", _logDir);
                _prm.SetStringParam("WorkDir", _workDir);
                _prm.SetStringParam("AssembliesDir", _assembliesDir);
                _prm.SetDateTimeParam("StartTime", DateTime.Now);
                _prm.Save();

                _dm4 = new DM4.DM4(_sysLog, _prm, _conn);

                WriteLog(String.Format("Open listener at address [{0}]...", _prm.GetStringParam("ListenerAddress")));

                Uri uri = new Uri(_prm.GetStringParam("ListenerAddress"));
                WriteLog("Uri = " + uri);

                _serviceHost = new ServiceHost(_dm4, uri);
                _serviceHost.AddServiceEndpoint(typeof(IDM4), new BasicHttpBinding(), uri);

                WriteLog("Core Uri = " + String.Format("{0}//{1}", uri, "Core"));
                _serviceHost.AddServiceEndpoint(typeof(IDM4Core), new BasicHttpBinding(), "Core");

                ServiceMetadataBehavior behavior = new ServiceMetadataBehavior { HttpGetEnabled = true };
                _serviceHost.Description.Behaviors.Add(behavior);
                _serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

                _serviceHost.Open();

                // Create the ServiceHost Log
                Uri uriLog = new Uri(String.Format("{0}LOG", uri));
                WriteLog("Log uri = " + uriLog);

                //string connectionLog = String.Format(@"metadata=res://*/DM4Model_Log.csdl|res://*/DM4Model_Log.ssdl|res://*/DM4Model_Log.msl;provider=System.Data.SqlClient;provider connection string=""{0}""", _conn.ConnectionString);
                //using (EntityConnection connection = new EntityConnection())
                //{
                //    Entities context = new Entities(connectionLog);
                //    context.AddObject("Entities", connection);
                //    context.SaveChanges();
                //}
                Entities.connectionLog = String.Format(@"metadata=res://*/DM4Model_Log.csdl|res://*/DM4Model_Log.ssdl|res://*/DM4Model_Log.msl;provider=System.Data.SqlClient;provider connection string=""{0}""", _conn.ConnectionString);

                _serviceLogHost = new WebServiceHost(typeof(LogDataservice), uriLog);
                WebHttpBinding binding = new WebHttpBinding();
                _serviceLogHost.AddServiceEndpoint(typeof(IRequestHandler), binding, "abc");
                _serviceLogHost.Open();

                WriteLog("Listener opened successfully");

                WriteLog("DM4 service start successfully!");
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, MessageType.Error);
                throw;
            }
        }

        protected override void OnStop()
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

            EventLog.WriteEntry("Stop DM4 service...");
            EventLog.WriteEntry("Close listener...");
            if ((_serviceHost != null) && (_serviceHost.State == CommunicationState.Opened))
                _serviceHost.Close();
            if ((_serviceLogHost != null) && (_serviceLogHost.State == CommunicationState.Opened))
                _serviceLogHost.Close();
            EventLog.WriteEntry("Listener closed");

            EventLog.WriteEntry("Shutdown engine...");
            if (_dm4 != null) _dm4.Shutdown();
            EventLog.WriteEntry("Shutdown engine complete");
        }
    }
}
